import java.io.StringWriter;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 *
 * @author erjoe
 */
public class Serializer
{
    public static Document SerializeObjectToXml(Object o, String nullStr) throws Exception
    {
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = domFactory.newDocumentBuilder();

        Document doc = builder.newDocument();
        doc.appendChild(XmlElementForObject(doc, o, nullStr));

        return doc;
    }

    static Element XmlElementForObject(Document doc, Object obj, String nullStr) throws Exception
    {
        if (obj == null)
        {
            return doc.createElement(nullStr);
        }
         if (isWrapperType(obj.getClass()) || obj instanceof String)
        {
            Element e = doc.createElement("p");
            //Element e = doc.createElement(obj.getClass().getSimpleName());
            e.setAttribute("t", obj.getClass().getSimpleName());
            e.setAttribute("v", obj.toString());
            return e;
        }

        if (obj.getClass().isArray())
        {
            Element e = doc.createElement("arr");

            Object[] arr = (Object[]) obj;
            for (Object o : arr)
            {
                e.appendChild(XmlElementForObject(doc, o, nullStr));
            }
            return e;
        }

        if (obj instanceof Collection)
        {
            Element e = doc.createElement("arr");

            Object[] arr = ((Collection) obj).toArray();
            for (Object o : arr)
            {
                e.appendChild(XmlElementForObject(doc, o, nullStr));
            }
            return e;
        }
//        if(obj instanceof java.util.LinkedHashMap.Entry){
//            
//        }

        if (obj instanceof Map)
        {
            Element e = doc.createElement("map");

            Object[] keys = ((Map) obj).keySet().toArray();
            for (Object key : keys)
            {
                Element i = doc.createElement("i");
                i.appendChild(XmlElementForObject(doc, key, nullStr));
                i.appendChild(XmlElementForObject(doc, ((Map) obj).get(key), nullStr));
                e.appendChild(i);
            }

            return e;
        }

        Element e = doc.createElement(obj.getClass().getSimpleName());

        for (java.lang.reflect.Field fld : obj.getClass().getFields())
        {
            if (fld.get(obj) == null)
            {
                Element f = doc.createElement(fld.getName());
                f.setAttribute("v", nullStr);
                e.appendChild(f);
                continue;
            }
            if (fld.getType().isPrimitive() || isWrapperType(fld.getType()) || fld.get(obj) instanceof String)
            {
                Element f = doc.createElement(fld.getName());
                f.setAttribute("t", fld.getType().getSimpleName());
                f.setAttribute("v", fld.get(obj).toString());
                e.appendChild(f);
                continue;
            }

            Element f = doc.createElement(fld.getName());
            f.appendChild(XmlElementForObject(doc, fld.get(obj), nullStr));
            e.appendChild(f);

        }
        return e;

    }

    public static String XmlDoxumentToString(Document doc) throws Exception
    {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(doc), new StreamResult(writer));
        return writer.getBuffer().toString().replaceAll("\n|\r", "");
    }

    public static boolean isWrapperType(Class<?> clazz)
    {
        return getWrapperTypes().contains(clazz);
    }

    private static HashSet<Class<?>> getWrapperTypes()
    {
        final HashSet<Class<?>> ret = new HashSet<>();
        ret.add(Boolean.class);
        ret.add(Character.class);
        ret.add(Byte.class);
        ret.add(Short.class);
        ret.add(Integer.class);
        ret.add(Long.class);
        ret.add(Float.class);
        ret.add(Double.class);
        ret.add(Void.class);
        return ret;
    }
}




